﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace GLDotNet.Toolkit
{
    public abstract class GLApplication : DisposableObject
    {
        IGLWindowInternal window;

        Stopwatch stopwatch;
        TimeSpan accumulatedTime;
        TimeSpan lastTime;

        public IGLWindow Window
        {
            get { return (IGLWindow)this.window; }
        }

        public GLContext GLContext
        {
            get { return this.window.GLContext; }
        }

        protected TimeSpan TargetElapsedTime
        {
            get;
            private set;
        }

        protected TimeSpan MaxElapsedTime
        {
            get;
            private set;
        }

        public GLApplication(GLContextParams contextParams)
            : this(contextParams, new GLContextListener())
        {
        }

        public GLApplication(GLContextParams contextParams, IGLContextListener contextListener)
        {
            this.window = new GLFWWindow(contextParams, contextListener);
            this.window.Initialize += this.WindowInternal_Initialize;
            this.window.Shutdown += this.WindowInternal_Shutdown;
            this.window.Idle += this.WindowInternal_Idle;

            this.TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 60);
            this.MaxElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 10);
        }

        protected override void DisposeManagedResources()
        {
            this.window.Initialize -= this.WindowInternal_Initialize;
            this.window.Shutdown -= this.WindowInternal_Shutdown;
            this.window.Idle -= this.WindowInternal_Idle;
            this.window.Dispose();            
        }

        protected override void DisposeUnmanagedResources()
        {
        }

        private void WindowInternal_Initialize(object sender, EventArgs e)
        {
            this.Initialize();
            this.stopwatch = Stopwatch.StartNew();
        }

        private void WindowInternal_Shutdown(object sender, EventArgs e)
        {
            this.Shutdown();
        }

        private void WindowInternal_Idle(object sender, EventArgs e)
        {
            TimeSpan currentTime = this.stopwatch.Elapsed;
            TimeSpan elapsedTime = currentTime - lastTime;
            lastTime = currentTime;

            if (elapsedTime > this.MaxElapsedTime)
                elapsedTime = this.MaxElapsedTime;

            this.accumulatedTime += elapsedTime;

            bool shouldDraw = false;

            while (this.accumulatedTime >= this.TargetElapsedTime)
            {
                this.Update(this.TargetElapsedTime);
                this.accumulatedTime -= this.TargetElapsedTime;

                shouldDraw = true;
            }

            if (shouldDraw)
                this.Draw();
        }

        public void Run()
        {
            this.window.Run();
        }

        public void Exit()
        {
            this.window.Exit();
        }

        protected virtual void Initialize()
        {
        }

        protected virtual void Update(TimeSpan elapsed)
        {
        }

        protected virtual void Draw()
        {
        }

        protected virtual void Shutdown()
        {
        }
    }
}
